package com.kn.keywordmatch.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.kn.keywordmatch.common.RedisKey;
import com.kn.keywordmatch.dto.req.KeywordItem;
import com.kn.keywordmatch.dto.req.MatchByKeywordReq;
import com.kn.keywordmatch.dto.resp.BaseResp;
import com.kn.keywordmatch.dto.resp.ParseKeywordExpressionResp;
import lombok.extern.slf4j.Slf4j;
import org.ahocorasick.trie.Emit;
import org.ahocorasick.trie.Trie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
@Slf4j
public class KeywordIndex {
    // 关键词索引
    public volatile ConcurrentHashMap<String, Set<String>> invertedIndex;
    // 表达式索引
    public volatile ConcurrentHashMap<String, Map<String,Set<Set<String>>>> expressionIndex;

    // 方案数据
    public volatile Map<Long, MatchByKeywordReq> keywordPlanMap;

    private volatile Trie mainTrie;
    private volatile Trie subTrie;
    private volatile Trie nextTrie;
    private volatile Trie excludeTrie;
    private volatile Trie ignoreTrie ;
    private volatile Trie expressionTrie ;
    private long lastUpdateTimestamp = 0;

    @Autowired
    @Qualifier("redisTemplateWithString")
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    @Lazy
    private MatchService matchService;

    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();


    public KeywordIndex() {
        this.invertedIndex = new ConcurrentHashMap<>();
        this.expressionIndex = new ConcurrentHashMap<>();
        this.mainTrie = null;  // 初始时 Trie 为空
        this.subTrie = null;  // 初始时 Trie 为空
        this.nextTrie = null;  // 初始时 Trie 为空
        this.excludeTrie = null; // 排除词的 Trie
        this.ignoreTrie  = null; // 忽略的 Trie
    }


    public void buildOrUpdateIndex(List<MatchByKeywordReq> keywordReqList) {
        Trie.TrieBuilder mainTrieBuilder = Trie.builder();
        Trie.TrieBuilder subTrieBuilder = Trie.builder();
        Trie.TrieBuilder nextTrieBuilder = Trie.builder();
        Trie.TrieBuilder excludeTrieBuilder = Trie.builder();
        Trie.TrieBuilder ignoreTrieBuilder = Trie.builder();
        Trie.TrieBuilder expressionTrieBuilder = Trie.builder();
        ConcurrentHashMap<String, Set<String>> newInvertedIndex = new ConcurrentHashMap<>();
        ConcurrentHashMap<String, Map<String,Set<Set<String>>>> newExpressionIndex = new ConcurrentHashMap<>();
        Map<Long, MatchByKeywordReq> newKeywordPlanMap = keywordReqList.stream().collect(Collectors.toMap(MatchByKeywordReq::getPlanID, Function.identity(),(k1,k2)->k2));

        // 全量构建倒排索引
        for (MatchByKeywordReq req : keywordReqList) {
            processKeywords(req, mainTrieBuilder, subTrieBuilder, nextTrieBuilder,
                    excludeTrieBuilder,ignoreTrieBuilder,expressionTrieBuilder,
                    newInvertedIndex,newExpressionIndex);
        }

        // 完成 Trie 构建
        Trie newMainTrie = mainTrieBuilder.build();
        Trie newSubTrie = subTrieBuilder.build();
        Trie newNextTrie = nextTrieBuilder.build();
        Trie newExcludeTrie = excludeTrieBuilder.build();
        Trie newExpressionTrieTrie = expressionTrieBuilder.build();
        Trie newIgnoreTrie = ignoreTrieBuilder.build();
        lastUpdateTimestamp = System.currentTimeMillis(); // 更新时间戳
        updateIndexAndTrie(newInvertedIndex,newExpressionIndex,newMainTrie,newSubTrie,newNextTrie,
                newExcludeTrie,newIgnoreTrie,newExpressionTrieTrie,newKeywordPlanMap);


    }
    public void updateIndexAndTrie(ConcurrentHashMap<String, Set<String>> newInvertedIndex, ConcurrentHashMap<String, Map<String, Set<Set<String>>>> newExpressionIndex, Trie newMainTrie, Trie newSubTrie, Trie newNextTrie, Trie newExcludeTrie, Trie newIgnoreTrie,
                                   Trie newExpressionTrie, Map<Long, MatchByKeywordReq> newKeywordPlanMap) {
        lock.writeLock().lock();
        try {
            // 在更新时，新的索引和 Trie 构建完成后再替换
            invertedIndex = newInvertedIndex;
            expressionIndex = newExpressionIndex;
            mainTrie = newMainTrie;
            subTrie = newSubTrie;
            nextTrie = newNextTrie;
            excludeTrie = newExcludeTrie;
            ignoreTrie = newIgnoreTrie;
            expressionTrie = newExpressionTrie;
            keywordPlanMap = newKeywordPlanMap;
        }finally {
            lock.writeLock().unlock();
        }
    }
    private void processKeywords(MatchByKeywordReq req, Trie.TrieBuilder mainTrieBuilder, Trie.TrieBuilder subTrieBuilder, Trie.TrieBuilder nextTrieBuilder,
                                 Trie.TrieBuilder excludeTrieBuilder, Trie.TrieBuilder ignoreTrieBuilder, Trie.TrieBuilder expressionTrieBuilder,
                                 ConcurrentHashMap<String, Set<String>> newInvertedIndex, ConcurrentHashMap<String, Map<String,Set<Set<String>>>> newExpressionIndex) {
        if (CollUtil.isNotEmpty(req.getMatchKeyword())) {
            for (KeywordItem item : req.getMatchKeyword()) {
                addKeywords("keyword1",req.getMonitorType(),item.getKeyword1(), mainTrieBuilder, req,newInvertedIndex, null);
                addKeywords("keyword2",req.getMonitorType(),item.getKeyword2(),subTrieBuilder, req,newInvertedIndex, null);
                addKeywords("keyword3",req.getMonitorType(),item.getKeyword3(),nextTrieBuilder, req,newInvertedIndex, null);
            }
        }
        addKeywords("expression",req.getMonitorType(),req.getKeywordExpression(), expressionTrieBuilder, req,newInvertedIndex,newExpressionIndex);
        addKeywords("exclude",req.getMonitorType(),req.getExcludeKeyword(),excludeTrieBuilder, req,newInvertedIndex, null);
        addKeywords("ignore",req.getMonitorType(),req.getIgnoreKeyword(),ignoreTrieBuilder, req,newInvertedIndex, null);
    }

    private void addKeywords(String flag, Integer monitorType, String keywords, Trie.TrieBuilder trieBuilder, MatchByKeywordReq req, ConcurrentHashMap<String, Set<String>> newInvertedIndex, ConcurrentHashMap<String, Map<String,Set<Set<String>>>> newExpressionIndex) {
        if (StrUtil.isNotBlank(keywords)) {
            Set<String> keywordSet = new HashSet<>();
            if(flag.equals("expression")){
                //处理表达式等
                ParseKeywordExpressionResp parseKeywordExpressionResp = analyzingExpression(keywords,"机构id:"+req.getAgencyID() + "方案id:" + req.getPlanID());
                Set<Set<String>> orSets = parseKeywordExpressionResp.getOrSets();
                Set<Set<String>> andSets = parseKeywordExpressionResp.getAndSets();
                if(Objects.isNull(orSets) && Objects.isNull(andSets)){
                    return;
                }
                if(Objects.nonNull(orSets) && !orSets.isEmpty()){
                    for (Set<String> orSet : orSets) {
                        keywordSet.addAll(orSet);
                    }
                }
                if(Objects.nonNull(andSets) &&!andSets.isEmpty()){
                    for (Set<String> andSet : andSets) {
                        keywordSet.addAll(andSet);
                    }
                }
                // 存储解析后的 方案id对应的映射关系
                if(newExpressionIndex !=null && !keywordSet.isEmpty()){
                    Map<String,Set<Set<String>>> map =new HashMap<>();
                    map.put("andSets",andSets);
                    map.put("orSets",orSets);
                    newExpressionIndex.put(req.getPlanID().toString(),map);
                }
            }else {
                if(0 == monitorType){
                    // 数解处理主副次等
                    keywordSet = Arrays.stream(keywords.split("\\s+"))
                            .map(String::trim)                  // 去掉前后空格
                            .filter(s -> !s.isEmpty())         // 过滤空字符串
                            .collect(Collectors.toSet());
                }if(1 == monitorType){
                    // 小极处理主副次等
                    keywordSet = Arrays.stream(keywords.split(";"))
                            .map(String::trim)                  // 去掉前后空格
                            .filter(s -> !s.isEmpty())         // 过滤空字符串
                            .collect(Collectors.toSet());
                }
            }

            keywordSet.forEach(s -> {
                trieBuilder.addKeyword(s);
                        newInvertedIndex.computeIfAbsent(flag + s, k -> ConcurrentHashMap.newKeySet()).add(req.getPlanID().toString());
                    });
        }
    }

    private ParseKeywordExpressionResp analyzingExpression(String keywordExpression,String msg) {
        // 解析表达式
        BaseResp<ParseKeywordExpressionResp> baseResp = matchService.analysisKeywordExpression(keywordExpression,msg);
        if(baseResp.getState() == 200 && baseResp.getData() != null){
            return baseResp.getData();
        }
        return new ParseKeywordExpressionResp();
    }

    public static void main(String[] args) {
        String a = " A B  C";
        Arrays.stream(a.split("\\s+")).forEach(b -> System.out.println(b));
    }
    public List<Emit> match(String text, String type) {
        // 这里可以根据类型调用不同的 Trie 匹配

        switch (type) {
            case "main":
                return (List<Emit>) mainTrie.parseText(text);
            case "sub":
                return (List<Emit>) subTrie.parseText(text);
            case "next":
                return (List<Emit>) nextTrie.parseText(text);
            case "exclude":
                return (List<Emit>) excludeTrie.parseText(text);
            case "ignore":
                return (List<Emit>) ignoreTrie.parseText(text);
            case "expression":
                return (List<Emit>) expressionTrie.parseText(text);
            default:
                return Collections.emptyList();
        }


    }

    public void checkAndUpdateTries() {
        if (mainTrie == null) {
            lock.writeLock().lock();
            try {
                if (mainTrie == null) { // 双重检查，确保唯一构建
                    List<MatchByKeywordReq> keywordReqList = getRedis(RedisKey.MONITOR_KEYWORD_REDIS_KEY);
                    buildOrUpdateIndex(keywordReqList);
                }
            } finally {
                lock.writeLock().unlock();
            }
        }
    }


    // 移除文本中的忽略词
    public String removeIgnoredWords(String text) {
        StringBuilder textBuilder = new StringBuilder(text);
        List<Emit> ignoreMatches = (List<Emit>) ignoreTrie.parseText(text);

        for (Emit emit : ignoreMatches) {
            int start = emit.getStart();
            int end = emit.getEnd() + 1; // end 是包含的索引，需要加 1
            textBuilder.delete(start, end);
        }

        return textBuilder.toString().replaceAll("\\s+", " ").trim(); // 清除多余空格
    }

    private List<MatchByKeywordReq> getRedis(String redisKey) {
        // 从 Redis 中获取整个哈希表
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(redisKey);
        List<MatchByKeywordReq> keywordReqList = new ArrayList<>();

        // 遍历条目并转换为 MatchByKeywordReq 对象
        for (Map.Entry<Object, Object> entry : entries.entrySet()) {
            if (entries.isEmpty()) {
                log.error("No data found in Redis for key: {}", redisKey);
                return keywordReqList; // 返回空列表或默认值
            }
            String jsonStr = (String) entry.getValue();
            if (jsonStr != null) {
                MatchByKeywordReq keywordReq = JSON.parseObject(jsonStr, MatchByKeywordReq.class);
                keywordReqList.add(keywordReq);
            }
        }

        return keywordReqList;
    }

    public Set<String> getPlansByKeyword(String keywordType,String keyword) {
        return invertedIndex.getOrDefault(keywordType + keyword, Collections.emptySet());
    }

    public Map<String,Set<Set<String>>> getKeywordSetByPlanId(String planId) {
        return expressionIndex.getOrDefault(planId, Collections.emptyMap());
    }

}
